﻿using System;
using System.IO;
using System.Collections.Generic;
using HAVNet.Intros;
using HAVNet.Intros.AST;
using HAVNet.Core.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace HAVNet.CoreTesting
{
    delegate void FoundASTObject(ASTNode node, Type type, int depth);
    delegate ASTStatement StmtWalkerFunction(ASTStatement stmt, Stack<ASTStatement> parents, int prof);

    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                foreach (string arg in args)
                {
                    if (arg == "tbus") tbus();
                    if (arg == "interfaces") interfaces();
                    if (arg == "enums") enums();
                    if (arg == "class") @class();
                    if (arg == "evacs") evacs();
                    if (arg == "statements") statements();
                    if (arg == "primlit") primlit();
                    if (arg == "namestypes") namestypes();
                    if (arg == "stop") stop();
                }
            }

            return;
        }

        static void tbus()
        {
            ASTBuilder ptb = new ASTBuilder();
            int c = 0;
            for (int i = 0; i < 1; ++i)
            {
                Dirs.walk(@"C:\FuentesCSharp\TecnoBusPortable", "*.cs", 0, new DirWalkerOnFound(
                    delegate(string path, int depth, bool isDir, bool err)
                    {
                        if (!isDir)
                        {
                            c++;
                            mostrar(path, ConsoleColor.Gray);

                            ASTCS3 cs3 = ptb.build(
                                path,
                                new string[] { "DEBUG", "PROGRAMADOR" }
                            );

                            Console.WriteLine(cs3);
                            Console.WriteLine(Environment.NewLine + Environment.NewLine + Environment.NewLine + Environment.NewLine);
                        }
                        return true;
                    }));
            }
        }

        static void interfaces()
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = ptb.build("..\\..\\PruebaInterfaces.cs", new string[] { "DEBUG", "PROGRAMADOR" });
            Console.WriteLine(cs3);
        }

        static void enums()
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = ptb.build("..\\..\\PruebaEnum.cs", new string[] { "DEBUG", "PROGRAMADOR" });
            Console.WriteLine(cs3);
        }

        static void @class()
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = ptb.build("..\\..\\PruebaClass.cs", new string[] { "DEBUG", "PROGRAMADOR" });
            Console.WriteLine(cs3);
        }

        static void evacs()
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = ptb.build("..\\..\\PruebaEventAccessors.cs", new string[] { "DEBUG", "PROGRAMADOR" });
            Console.WriteLine(cs3);
        }

        static void statements()
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = ptb.build("..\\..\\PruebaStatements.cs", new string[] { "DEBUG", "PROGRAMADOR" });
            Console.WriteLine(cs3);
        }

        static void primlit()
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = ptb.build("..\\..\\PruebaPrimLit.cs", new string[] { "DEBUG", "PROGRAMADOR" });
            Console.WriteLine(cs3);
        }

        static void namestypes()
        {
            ASTBuilder ptb = new ASTBuilder();
            string s = string.Format("");

            for (int i = 0; i < 1; ++i)
            {
                string sdir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "ConsoleApplication1");

                //Dirs.walk(@"C:\FuentesCSharp\TecnoBusPortable", "*.cs", 0,
                //Dirs.walk (@"C:\FuentesCSharp\", "*.cs", 0,
                Dirs.walk(sdir, "Program.cs", 0,
                    //Dirs.walk (@"C:\Documents and Settings\avarela\Escritorio\ConsoleApplication1", "*.cs", 0,
                    //Dirs.walk (@"C:\Users\hav\Desktop\ConsoleApplication1", "*.cs", 0,
                    //Dirs.walk (@"C:\Documents and Settings\avarela\Escritorio\ConsoleApplication1", "Program.cs", 0,
                    //Dirs.walk(@"C:\FuentesCSharp\TecnoBusPortable\TecnoBus.Comunicacion", "ConsumidorWsLogueos.cs", 0,
                    //Dirs.walk(@"C:\FuentesCSharp\TecnoBusPortable\TecnoBus.Presentacion", "FrmPrincipal.cs", 0,
                (path, depth, isDir, err) =>
                {
                    if (!isDir)
                    {
                        Console.WriteLine("--------------- " + path + "");

                        ASTCS3 cs3 = ptb.build(path, new string[] { "" });

                        WalkerClasses.get(
                            cs3.namespaceMemberDecls,
                            (@class, depthx) =>
                            {
                                WalkerClassesMethods.get(
                                    @class,
                                    (method) =>
                                    {
                                        if (method.block != null)
                                        {
                                            method.block.stmts.Insert (0, dameFuncCall(method));
                                            
                                            for (int j = 0; j < method.block.stmts.Count; ++j)
                                            {
                                                stmtWalk(method.block.stmts[j],
                                                    (stmt, stack, prof) =>
                                                    {
                                                        if (stmt is ASTEmbStmtReturn)
                                                            stmt = transformarReturn(@class, method, stmt);
                                                        else if (stmt is ASTEmbStmtThrow)
                                                            return transformarThrow(@class, method, stmt);

                                                        return null;
                                                    },
                                                0);

                                                if (method.block.stmts[j] is ASTEmbStmtReturn)
                                                    method.block.stmts[j] = transformarReturn(@class, method, method.block.stmts[j]);
                                                else if (method.block.stmts[j] is ASTEmbStmtThrow)
                                                    method.block.stmts[j] = transformarThrow(@class, method, method.block.stmts[j]);
                                                else if (method.block.stmts[j] is ASTEmbStmtIf)
                                                    if (((ASTEmbStmtIf)method.block.stmts[j]).thenStatement is ASTEmbStmtReturn)
                                                        ((ASTEmbStmtIf)method.block.stmts[j]).thenStatement = transformarReturn(@class, method, ((ASTEmbStmtIf)method.block.stmts[j]).thenStatement);
                                            }
                                        }
                                    }
                                );
                            },
                        0);

                        string sResult = cs3.ToString();
                        Console.WriteLine(sResult);
                    }
                    return true;
                });
            }
        }

        /*
         *  En el trayecto habló de literatura, probablemente con sinceridad y con fervor.
         *  Me dije: Él es el literato; yo soy un hombre cansado, frívolamente preocupado con una mujer.
         */

        private static ASTStatement transformarThrow(ASTTypeDeclClass @class, ASTMemberDeclMethod method, ASTStatement @throw)
        {
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(@class.name + " >>> " + method.name + " ::: " + @throw.ToString());
            Console.ForegroundColor = ConsoleColor.Gray;

            return null;
        }

        private static ASTStatement transformarReturn(ASTTypeDeclClass @class, ASTMemberDeclMethod method, ASTStatement @return)
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = null;

            string sCodigo =
            @"
                namespace Foo {
                    class ClassFoo {
                        AlgunTipo methodFoo()
                        {
                         // este es el statement que quiero robar :)
                         // |
                         // v
                            {
                                <TIPO_RET> _____hav = <EXPRESION> ;
                                return _____hav ;
                            }
                        }
                    }
                }
            ";

            sCodigo = sCodigo.Replace("<TIPO_RET>", method.returnType.ToString());
            sCodigo = sCodigo.Replace("<EXPRESION>", ((ASTEmbStmtReturn)@return).expression.ToString());

            // crea el AST del statement...
            using (Stream s = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sCodigo)))
                cs3 = ptb.build(s, new string[] { "DEBUG", "PROGRAMADOR" });

            ASTStatement stmtAux = null;

            WalkerClasses.get(
                cs3.namespaceMemberDecls,
                    (xclass, depthx) =>
                    {
                        WalkerClassesMethods.get(
                            xclass,
                            new ClassMethodFound(
                                delegate(ASTMemberDeclMethod methodX)
                                {
                                    if (methodX.block != null)
                                        if (methodX.block.stmts.Count > 0)
                                            stmtAux = methodX.block.stmts[0];
                                }));
                    }
                ,
            0);

            return stmtAux;
        }

        // idea para otro walker...
        //  -tiene un stmt para empezar a recorrer...
        //  -tiene un predicado, para ver si se da cierta condicion...
        //  -tiene un stmtnew para reemplazar algo si se da ese predicado...


        static void stmtWalk(ASTStatement stmtx, StmtWalkerFunction f, int prof)
        {
            if (stmtx is ASTEmbStmtBlock)
            {
                f(stmtx, null, prof);

                //foreach (ASTStatement xx in ((ASTEmbStmtBlock)stmtx).block.stmts)
                for (int i = 0; i < ((ASTEmbStmtBlock)stmtx).block.stmts.Count; ++i)
                {
                    // esto anda!
                    //if (((ASTEmbStmtBlock)stmtx).block.stmts[i] is ASTEmbStmtReturn)
                    //    ((ASTEmbStmtBlock)stmtx).block.stmts[i] = null;

                    // esto tambien!
                    //if (((ASTEmbStmtBlock)stmtx).block.stmts[i] is ASTEmbStmtReturn)
                    //{
                    //    ((ASTEmbStmtBlock)stmtx).block.stmts.Remove(((ASTEmbStmtBlock)stmtx).block.stmts[i]);
                    //    return;
                    //}

                    stmtWalk(((ASTEmbStmtBlock)stmtx).block.stmts[i], f, prof + 1);
                }
            }
            else if (stmtx is ASTEmbStmtBreak)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtChecked)
            {
                f(stmtx, null, prof);

                foreach (ASTStatement xx in ((ASTEmbStmtChecked)stmtx).block.stmts)
                    stmtWalk(xx, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtContinue)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtDo)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtDo)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtFixed)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtFixed)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtFor)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtFor)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtForeach)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtForeach)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtGoto)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtIf)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtIf)stmtx).thenStatement, f, prof + 1);
                stmtWalk(((ASTEmbStmtIf)stmtx).elseStatement, f, prof + 1);

                if (((ASTEmbStmtIf)stmtx).elseStatement is ASTEmbStmtReturn)
                    ((ASTEmbStmtIf)stmtx).elseStatement = null;
            }
            else if (stmtx is ASTEmbStmtLock)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtLock)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtReturn)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtSemicolon)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtStatementExpression)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtSwitch)
            {
                f(stmtx, null, prof);

                foreach (ASTSwitchSection switchSection in ((ASTEmbStmtSwitch)stmtx).switchSections)
                    foreach (ASTStatement xx in switchSection.statements)
                        stmtWalk(xx, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtThrow)
            {
                f(stmtx, null, prof);
            }
            else if (stmtx is ASTEmbStmtTry)
            {
                f(stmtx, null, prof);

                foreach (ASTStatement xx in ((ASTEmbStmtTry)stmtx).tryBlock.stmts)
                    stmtWalk(xx, f, prof + 1);

                foreach (ASTCatch @catch in ((ASTEmbStmtTry)stmtx).catchs)
                    foreach (ASTStatement xx in @catch.block.stmts)
                        stmtWalk(xx, f, prof + 1);

                if (((ASTEmbStmtTry)stmtx).finallyBlock != null)
                    foreach (ASTStatement xx in ((ASTEmbStmtTry)stmtx).finallyBlock.stmts)
                        stmtWalk(xx, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtUnchecked)
            {
                f(stmtx, null, prof);

                foreach (ASTStatement xx in ((ASTEmbStmtUnchecked)stmtx).block.stmts)
                    stmtWalk(xx, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtUnsafe)
            {
                f(stmtx, null, prof);

                foreach (ASTStatement xx in ((ASTEmbStmtUnsafe)stmtx).block.stmts)
                    stmtWalk(xx, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtUsing)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtUsing)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtWhile)
            {
                f(stmtx, null, prof);

                stmtWalk(((ASTEmbStmtWhile)stmtx).statement, f, prof + 1);
            }
            else if (stmtx is ASTEmbStmtYield)
            {
                f(stmtx, null, prof);
            }
        }


        static ASTStatement dameFuncCall(ASTMemberDeclMethod method)
        {
            ASTBuilder ptb = new ASTBuilder();
            ASTCS3 cs3 = null;

            string sCodigo =
            @"
                namespace Foo {
                    class ClassFoo {
                        void methodFoo(){
                            Trace.enterMethod  (
                                System.Threading.Thread.CurrentThread.ManagedThreadId,
                                System.Threading.Thread.CurrentThread.Name,
                                ""<PARAM0>"",
                                ""<PARAM1>"",
                                  <PARAM3>
                            ); } } }
            ";

            sCodigo = sCodigo.Replace("<PARAM0>", method.name.ToString());
            sCodigo = sCodigo.Replace("<PARAM1>", method.returnType.ToString());
            sCodigo = sCodigo.Replace("<PARAM3>", dameValoresParametros(method.formalParamList));

            // crea el AST del statement...
            using (Stream s = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(sCodigo)))
                cs3 = ptb.build(s, new string[] { "DEBUG", "PROGRAMADOR" });

            ASTStatement stmtAux = null;

            WalkerClasses.get(
                cs3.namespaceMemberDecls,
                new ClassFound(
                    delegate(ASTTypeDeclClass @class, int depthx)
                    {
                        WalkerClassesMethods.get(
                            @class,
                            new ClassMethodFound(
                                delegate(ASTMemberDeclMethod methodX)
                                {
                                    if (methodX.block != null)
                                        if (methodX.block.stmts.Count > 0)
                                            stmtAux = methodX.block.stmts[0];
                                }));
                    }
                ),
            0);

            return stmtAux;
        }

        private static string dameValoresParametros(ASTFormalParamList fpl)
        {
            string sRet = @"string.Format (   ";

            string sFormat = @"""";

            for (int i = 0; i < fpl.Count; ++i)
                sFormat += fpl[i].ident + "={" + i + "} ";

            sFormat += @"""";

            sRet += sFormat;

            foreach (ASTFormalParam fp in fpl)
                sRet += ", Trace.paramProcessor(" + fp.ident + ")";

            sRet += ")";

            return sRet;
        }

        static void stop()
        {
            Console.ReadLine();
        }

        static void mostrar(string s)
        {
            Console.WriteLine(s);
        }

        static void mostrar(string s, ConsoleColor cc)
        {
            Console.ForegroundColor = cc;
            Console.WriteLine(s);
        }
    }
}

